var Projectile = require('js/projectile').Projectile;
var GameEntity = require('js/gameEntity').GameEntity;
var utils = require('js/utils');
var gamejs = require('gamejs');

/**
 * Creates a Turret object
 *
 * @param {Match} match The match on witch the turret will "work"
 * @param {[number, number]} coordinates The coordinates of the sprite center
 *                           The format is[x, y], with x=column and y=row
 * @param {Function} tf The targetting function that the turret will use to
 *                   select which enemy to shoot first. For some functions
 *                   examples see Turret.TARGETTING_FUNCTIONS.
 *                   [default Turret.TARGETTING_FUNCTIONS.nearest]
 */
var Turret = exports.Turret = function(match, coordinates, tf) {
	// Execute GameEntity constructor
	Turret.superConstructor.call(this, match, coordinates,
		Turret.UPGRADE_LIST, true
	);
	// Set some instance attributes
	this.targettingFunction = tf || Turret.TARGETTING_FUNCTIONS.nearest;

	// The time (in ms) to wait before shooting again
	this._reloading = 0;

	// Coordinates of the Turret weapon location (for a better animation)
	this._weaponCoordinates = [
		this.rect.center[0] - 17,
		this.rect.center[1] - 3
	];
};

/**
 * Turret extends GameEntity
 */
gamejs.utils.objects.extend(Turret, GameEntity);

/**
 * The list of available upgrades
 */
Turret.UPGRADE_LIST = [
	// Level 0
	{price: 100, range: 100, reloadTime: 2000, resaleValue: 70,
		image: IMAGE_ROOT + "turret.png", imageShape: 'circular',
		projectilePower: 1
	},
	// Level 1
	{price: 75, range: 120, reloadTime: 900, resaleValue: 100,
		image: IMAGE_ROOT + "turret1.png", imageShape: 'circular',
		projectilePower: 2
	}
];

/**
 * This class field saves the price needed to buy a new Turret
 */
Turret.BUY_PRICE = Turret.UPGRADE_LIST[0].price;

/**
 * The list of available targetting functions
 */
Turret.TARGETTING_FUNCTIONS = {
	// Use this to target the enemy nearest the planet
	nearest: function(enemy1, enemy2) {
		var enemy1X = enemy1.rect.center[0];
		var enemy2X = enemy2.rect.center[0];
		return (enemy1X <= enemy2X) ? enemy1 : enemy2;
	},
	// Use this to target the enemy with more hp
	strongest: function(enemy1, enemy2) {
		return (enemy1.health >= enemy2.health) ? enemy1 : enemy2;
	},
	// Use this to target the enemy with less hp
	weakest: function(enemy1, enemy2) {
		return (enemy1.health <= enemy2.health) ? enemy1 : enemy2;
	}
};

/**
 * Returns the targetting function used by this Turret
 *
 * @returns {Function}
 */
Turret.prototype.getTargettingFunction = function() {
	return this.targettingFunction;
};

/**
 * Set the targetting function used by this Turret. For some examples see
 * Turret.TARGETTING_FUNCTION
 *
 * @param {Function} targettingFunction the new targetting function to use
 */
Turret.prototype.setTargettingFunction = function(targettingFunction) {
	this.targettingFunction = targettingFunction;
};

/**
 * Returns the length of the turret shooting range
 *
 * @return {number} The length of the shooting range (in pixels)
 */
Turret.prototype.getRange = function() {
	return this.getLevelSettings().range;
};

/**
 * Returns the delay between a shoot and the next one
 *
 * @return {number} The shooting delay (in milliseconds)
 */
Turret.prototype.getReloadTime = function() {
	return this.getLevelSettings().reloadTime;
};

/**
 * Returns the turret's projectiles power
 *
 * @return {number}
 */
Turret.prototype.getProjectilePower = function() {
	return this.getLevelSettings().projectilePower;
};

/**
 * Return the coordinates of the weapon
 *
 * @return {[Number, Number]} The [x, y] coordinates of the turret weapon
 */
Turret.prototype.getWeaponCoordinates = function() {
	return this._weaponCoordinates;
};

/**
 * Updates the turret status by detecting if it has to shoot or the shooting
 * ray position if already shooting
 *
 * @param {number} msDuration The time past from the last call (in ms)
 */
Turret.prototype.update = function(msDuration) {
	// If turret is still reloading update _reloading
	if (this._reloading - msDuration > 0) {
		this._reloading = this._reloading - msDuration;
	}
	// else Turret can shoot
	else {
		// If a target is available shoot it down
		var target = this._targetEnemy();
		if (target) {
			this.getMatchMap().addProjectile(
				new Projectile(target, this, this.getProjectilePower())
			);
			// Now the turret needs to reload
			this._reloading = this.getReloadTime();
		}
		// if there isn't a target keep the turret ready to shoot
		else {
			this._reloading = 0;
		}
	}
};

/**
 * Target one of the visible and in-range enemy using the function
 * this.targettingFunction
 *
 * @return {Enemy|null} return the selected enemy or null if there isn't an
 *                      enemy visible and in firing range
 */
Turret.prototype._targetEnemy = function() {
	// Get the Array containing all the enemies
	var enemies = this.getMatchMap().getEnemies().sprites();
	// Keep only the good ones (visible and in firing range)
	var goodEnemies = [];
	for (var i = 0; i < enemies.length; i++) {
		if (enemies[i].isDetected() && this._checkRangeEnemy(enemies[i])) {
			goodEnemies.push(enemies[i]);
		}
	}
	if (goodEnemies.length === 0) {
		// No enemy visible and in range
		return null;
	}
	else {
		// Select one enemy between the good ones with the provided function
		return goodEnemies.reduce(this.targettingFunction);
	}
};

/**
 * Checks if enemy is in firing range
 *
 * @param {Sprite} enemy
 * @return {Boolean} true if enemy in in range, false otherwise
 */
Turret.prototype._checkRangeEnemy = function(enemy) {
	var distance = utils.distance(
		enemy.rect.center, this.getCenter()
	);
	return distance <= this.getRange();
};